/*
 * =====================================================================================
 *
 *       Filename:  pre_process_server.cpp
 *
 *    Description:  TCP预先派生子进程服务器程序，accept上无锁保护
					优点: 无须引人父进程执行fork的开销就可以处理新到的客户
					缺点：必须预先知道有多少客户，如果子进程数不够在新到得到客户被忽略
					但可以增加代码，实现动态管理
					
					另外在这种模型中，所有的子进程都等待一个socket上，当连接来临时，会全部
					都被唤醒，最终只有一个子进程获取连接，其他的子进程再度睡眠。这样存在惊群
					的性能问题。可以采取：
					1. 给accept加文件锁的方式
					2. 给accept加线程上锁
					3. accept在主线程中完成，主线程寻找空闲子线程，来处理新的连接
 *
 *        Version:  1.0
 *        Created:  2008年09月17日 17时24分36秒
 *       Revision:  none
 *       Compiler:  g++
 *
 *         Author:  ZhixiongHong (hzx), fishwarter@gmail.com
 *        Company:  Unioncast
 *
 * =====================================================================================
 */

#include "cs_common.h"

static int 		nChildren; // 预先分配的进程数目
static pid_t	*pids;


int main(int argc, char** argv)
{
	if( argc !=3 )	
	{
		cout << "usage: " << argv[0] << " <#port> <#children>" << endl;
		return -1;
	}
	unsigned short port = (unsigned short )atoi(argv[1]);
	nChildren = atoi(argv[2]);
	

    int         listenfd;
    struct      sockaddr_in servaddr;

	signal(SIGCHLD, sig_chld);
    signal(SIGPIPE, SIG_IGN);

	listenfd = socket(AF_INET, SOCK_STREAM,0);
    if(listenfd < 0)
    {
        cout << "create listenfd error " << strerror(errno) << endl;
        return -1;
    }

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(port);
    
    const int on = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    int retint = bind(listenfd, (sockaddr *)&servaddr, sizeof(servaddr));
    if( retint)
    {
        cout << "bind listenfd error: "  << strerror(errno) << endl;
        return -1;
    }
    
    retint = listen(listenfd, LISTENQ);
    if( retint)
    {
        cout << "listen error " << strerror(errno) << endl;
        return -1;
    }
	
    cout << "in " << argv[0] << "start svr at " << port << endl;
	
	pids = (pid_t* )malloc(nChildren*sizeof(pid_t));
	
	pid_t child_make(int, int, int);
	socklen_t addrlen = sizeof(servaddr);
	for(int i = 0; i < nChildren; i++)
		pids[i] = child_make(i, listenfd, addrlen);

	signal(SIGINT, sig_int);

    for(; ;)
		pause();
}



/*
 * 创建子进程
 */
pid_t child_make(int i , int listenfd, int addrlen)
{
	pid_t pid;
	void child_main(int, int, int);

	pid = fork();
	if( pid >0 )
		return pid; /* parent  */
	 
	child_main(i, listenfd, addrlen); /*  never return, child thread */
}	

/* 
 * i 位进程数组的索引
 */
void child_main(int i, int listenfd, int addrlen) 
{
	int 		connfd;
	socklen_t	clilen;
	struct sockaddr* cliaddr;
	cliaddr = (struct sockaddr *)malloc(addrlen);
	printf("child %ld starting\n", (long)getpid());
	for(;;)
	{
		clilen = addrlen;
		connfd = accept(listenfd, cliaddr, &clilen); /* 多个进程在同一个监听socket上accept */
		printf("child %ld accept a connection\n", (long)getpid());
		web_child(connfd);
		close(connfd);
	}
}

void sig_int(int signo)
{
	int i = 0;
	void pr_cpu_time(void);
	for(i = 0; i < nChildren; i++) /* kill all child threads*/
		kill(pids[i], SIGTERM);
	
	while( wait(NULL) > 0 )
		;
	
	if( errno != ECHILD)
		cout << "wait error" << endl;
	pr_cpu_time(); /* statistics cpu */
	exit(0);
}

void sig_chld(int signo)
{
	
}


